#pragma once

#include <string>
#include <stdexcept>

#include "../../sqlite/sqlite3.h"

namespace CQLIB {
	namespace CQSQL {

		class sqlite_exception : public std::runtime_error {
		public:
			sqlite_exception(const char* msg, std::string sql, int code = -1) : runtime_error(msg), code(code), sql(sql) {}
			sqlite_exception(int code, std::string sql) : runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
			int get_code() const { return code & 0xFF; }
			int get_extended_code() const { return code; }
			std::string get_sql() const { return sql; }
		private:
			int code;
			std::string sql;
		};

		namespace errors {
			//One more or less trivial derived error class for each SQLITE error.
			//Note the following are not errors so have no classes:
			//SQLITE_OK, SQLITE_NOTICE, SQLITE_WARNING, SQLITE_ROW, SQLITE_DONE
			//
			//Note these names are exact matches to the names of the SQLITE error codes.
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
		class name: public sqlite_exception { using sqlite_exception::sqlite_exception; };\
		derived
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
		class base ## _ ## sub: public base { using base::base; };
#include "lists/error_codes.h"
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE

		//Some additional errors are here for the C++ interface
			class more_rows : public sqlite_exception { using sqlite_exception::sqlite_exception; };
			class no_rows : public sqlite_exception { using sqlite_exception::sqlite_exception; };
			class more_statements : public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement
			class invalid_utf16 : public sqlite_exception { using sqlite_exception::sqlite_exception; };

			static void throw_sqlite_error(const int& error_code, const std::string& sql = "") {
				switch (error_code & 0xFF) {
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived)     \
				case SQLITE_ ## NAME: switch(error_code) {          \
					derived                                           \
					default: throw name(error_code, sql); \
				}
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
					case SQLITE_ ## BASE ## _ ## SUB: throw base ## _ ## sub(error_code, sql);
#include "lists/error_codes.h"
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE
				default: throw sqlite_exception(error_code, sql);
				}
			}
		}
		namespace exceptions = errors;
	}
}